This notebook is a follow up notebook to previous benchmarking done in 02-compare-quants.RMD. Here, we have used the most recent release of Salmon and Cellranger 6.0.0, and included the new Alevin-Fry method. Alevin-Fry can use either a selective alignment strategy or pseudoalignment strategy (with the --sketch flag),so here we are testing both strategies. Alevin-Fry also can use the 10X whitelist for barcode assignment rather than generating the putative whitelist using the --unfiltered-pl flag, so we have also included that in our tests.

The resolution currently used for Alevin-Fry is the Full resolution. See the documentation for more information on different options for resolution that could be used.

The goal of this notebook is to compare the quantification performed by each tool across the samples we have currently run. Thus far, we have 4 10Xv3 single cell RNA-seq samples that have been run on all tools, SCPCR000003, SCPCR000006, SCPCR000126, and SCPCR000127. We also have 2 10Xv3.1 single nucleus RNA-seq samples that have been run on all tools, SCPCR000118 and SCPCR000119. All the snRNA-seq samples have been run using either the pre-mRNA index or the mRNA index. One thing to note is that the new spliced index is different from the previously used index’s with the single cell RNA seq samples in that it was created by grabbing the genomic regions corresponding to spliced cDNA only from the ensembl gtf, rather than taking the cDNA.fasta directly from ensembl.

Setup

Load Libraries for import

library(tidyverse)
library(ggplot2)
library(SingleCellExperiment)
library(ggforce)

File and Directory Setup

# where all the data lives after running 01-import-quant-data.Rmd
import_dir <- file.path('data')

quant_info_file <- file.path(import_dir, 'quant_info.tsv')

alevin_rds <- file.path(import_dir, 'alevin_sces.rds')
alevin_fry_rds <- file.path(import_dir, 'alevin_fry_sces.rds')
alevin_fry_sketch_rds <- file.path(import_dir, 'alevin_fry_sketch_sces.rds')
alevin_fry_unfiltered_rds <- file.path(import_dir, 'alevin_fry_unfiltered_sces.rds')
alevin_fry_unfiltered_sketch_rds <- file.path(import_dir, 'alevin_fry_unfiltered_sketch_sces.rds')
kallisto_rds <- file.path(import_dir, 'kallisto_sces.rds')
cellranger_rds <- file.path(import_dir, 'cellranger_sces.rds')
# read in quant info file created in 01-import-quant-data.Rmd
quant_info <- readr::read_tsv(quant_info_file)

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  tool = col_character(),
  quant_dir = col_character(),
  sample = col_character(),
  index_type = col_character(),
  kmer = col_character(),
  decoy = col_character(),
  alevin_alignment = col_character(),
  alevin_permit_list = col_character(),
  scpca_sample_id = col_character(),
  seq_unit = col_character(),
  technology = col_character()
)
# make sure that the tool is labeled as alevin_fry_sketch/ unfiltered to distinguish from regular alevin_fry for later
quant_info[which(quant_info$alevin_alignment == "sketch" & quant_info$alevin_permit_list != "unfiltered"),"tool"] <- "alevin_fry_sketch"
quant_info[which(quant_info$alevin_alignment == "salign" & quant_info$alevin_permit_list == "unfiltered"), "tool"] <- "alevin_fry_unfiltered"
quant_info[which(quant_info$alevin_alignment == "sketch" & quant_info$alevin_permit_list == "unfiltered"), "tool"] <- "alevin_fry_unfiltered_sketch"

# save changes to quant file
quant_info_file_update <- file.path(import_dir, 'quant_info_update.tsv')
readr::write_tsv(quant_info, file.path(quant_info_file_update))
# Each RDS file contains a SingleCellExperiment
alevin_sces <- readr::read_rds(alevin_rds)
alevin_fry_sces <- readr::read_rds(alevin_fry_rds)
alevin_fry_sketch_sces <- readr::read_rds(alevin_fry_sketch_rds)
alevin_fry_unfiltered_sces <- readr::read_rds(alevin_fry_unfiltered_rds)
alevin_fry_unfiltered_sketch_sces <- readr::read_rds(alevin_fry_unfiltered_sketch_rds)
kallisto_sces <- readr::read_rds(kallisto_rds)
cellranger_sces <- readr::read_rds(cellranger_rds)
## get all annotation data from S3
annotation_dir <- file.path(import_dir, "annotation")

## get mitochondrial gene list from s3
annotation_files_s3 <- 
  's3://nextflow-ccdl-data/reference/homo_sapiens/ensembl-100/annotation/'

sync_call <- paste('aws s3 sync', annotation_files_s3, annotation_dir)
system(sync_call, ignore.stdout = TRUE)
mito_file <- file.path(annotation_dir, "Homo_sapiens.ensembl.100.mitogenes.txt")

mito_genes <- readr::read_tsv(mito_file, col_names = "gene_id")

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  gene_id = col_character()
)
mito_genes <- mito_genes %>%
  pull(gene_id)

Add QC Metrics

addPerCellQC_mito <- function(sce, mito = mito_genes){
  # add per cell QC with mitochondrial genes separately for later comparisons
  scater::addPerCellQC(
    sce, 
    subsets = list(mito = mito[mito %in% rownames(sce)])
  )
}
alevin_sces <- alevin_sces %>% 
  purrr::map(addPerCellQC_mito)

alevin_fry_sces <- alevin_fry_sces %>%
  purrr::map(addPerCellQC_mito)

alevin_fry_sketch_sces <- alevin_fry_sketch_sces %>%
  purrr::map(addPerCellQC_mito)

alevin_fry_unfiltered_sces <- alevin_fry_unfiltered_sces %>%
  purrr::map(addPerCellQC_mito)

alevin_fry_unfiltered_sketch_sces <- alevin_fry_unfiltered_sketch_sces %>%
  purrr::map(addPerCellQC_mito)

kallisto_sces <- kallisto_sces %>% 
  purrr::map(addPerCellQC_mito)

cellranger_sces <- cellranger_sces %>% 
  purrr::map(addPerCellQC_mito)
Loading required package: HDF5Array
Loading required package: DelayedArray
Loading required package: Matrix

Attaching package: ‘Matrix’

The following object is masked from ‘package:S4Vectors’:

    expand

The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack


Attaching package: ‘DelayedArray’

The following object is masked from ‘package:purrr’:

    simplify

The following objects are masked from ‘package:base’:

    aperm, apply, rowsum

Loading required package: rhdf5
## merge all QC output into one data frame to work with for plotting comparisons
celldata_to_df <- function(sce){
  # extract the column (cell) summary data from a SCE, 
  # convert to data frame and move cell id to a column
  as.data.frame(colData(sce)) %>%
  tibble::rownames_to_column(var = "cell_id") %>%
    # add a column to get # of cells detected in that sample
    mutate(cells_detected = ncol(sce))
}

alevin_cell_qc <- alevin_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

alevin_fry_cell_qc <- alevin_fry_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

alevin_fry_sketch_cell_qc <- alevin_fry_sketch_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

alevin_fry_unfiltered_cell_qc <- alevin_fry_unfiltered_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

alevin_fry_unfiltered_sketch_cell_qc <- alevin_fry_unfiltered_sketch_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

kallisto_cell_qc <- kallisto_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

cellranger_cell_qc <- cellranger_sces %>%
  purrr::map_df(celldata_to_df, .id = "quant_id")

# combine all the data frames into one
cell_qc <- dplyr::bind_rows(
  alevin = alevin_cell_qc,
  alevin_fry = alevin_fry_cell_qc,
  alevin_fry_sketch = alevin_fry_sketch_cell_qc,
  alevin_fry_unfiltered = alevin_fry_unfiltered_cell_qc,
  alevin_fry_unfiltered_sketch = alevin_fry_unfiltered_sketch_cell_qc,
  kallisto = kallisto_cell_qc,
  cellranger = cellranger_cell_qc,
  .id = "tool"
) %>%
  dplyr::left_join(quant_info,
                   by = c("tool" = "tool", 
                          "quant_id" = "quant_dir")) %>%
  # remove extraneous run from 119 with txome index since we have the run with the spliced index
  dplyr::filter(quant_id != "SCPCR000119-txome_k31") %>%
  # remove duplicate runs of alevin-fry --knee on 118, 119
  dplyr::filter(quant_id !="SCPCR000118-spliced_intron_txome_k31-salign") %>%
  dplyr::filter(quant_id != "SCPCR000119-spliced_intron_txome_k31-salign")
                  

Comparison of QC Metrics across all tools

Per Cell QC

Let’s first start by taking a look at some general information across all of our tools and how consistent each of these metrics is for all four of the samples. Here are the metrics we want to look at:

  • Total number of cells detected
  • Mitochondrial content/ cell
  • Number of UMIs/cell
  • Number of Genes/cell
## make new data frame with mean of cells detected and standard dev
cells_detected_stats <- cell_qc %>%
  group_by(sample, tool) %>%
  summarise(cells_detected = mean(cells_detected))
`summarise()` has grouped output by 'sample'. You can override using the `.groups` argument.
cells_detected_stats

## first look at cells detected
ggplot(cells_detected_stats, aes(x = tool, y = cells_detected)) +
  geom_jitter(mapping = aes(color = sample)) +
  geom_violin() +
  theme_classic() +
  ylab("Total Cells Detected") + 
  xlab("") + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Based on this, it looks like alevin-fry using the --knee-distance does not handle single-nuclei samples well. In contrast, the alevin-fry --unfiltered-pl and kallisto samples were filtered using DropletUtils::emptyDrops and they have much lower numbers for the same single-nuclei samples.

Let’s take a look at the mitochondrial content per cell across the tools and see if we see variation there.

ggplot(cell_qc, aes(x = tool, y = subsets_mito_percent, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) + 
  theme_classic() + 
  ylab("% Mito /Cell") + 
  theme(axis.ticks.x = element_blank(), axis.text.x = element_text(angle = 45, hjust = 1))

It looks like SCPCR000003 is a very poor sample across all tools and has a lot of dead cells. It might be a good idea to remove that from our comparisons and not consider this for benchmarking. Additionally, it looks like we can see that SCPCR000118 (snRNAseq) also has some variation, but the other samples tend to be fairly consistent.

# remove SCPCR000003 from cell_qc for the rest of the plotting 
cell_qc_filter <- cell_qc %>%
  dplyr::filter(sample != "SCPCR000003")

Now let’s look at the number of UMI’s detected/cell that we’ve removed our outlier sample.

# number of UMI's/sample
ggplot(cell_qc_filter, aes(x = tool, y = sum, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/cell") + 
  xlab("")

At first glance, SCPCR000118 and SCPCR000119 (the single nucleus RNA-seq samples) have very low values of UMI/cell. Is this because these samples are poor quality? Or is it a problem with the index? However, the samples don’t look any better with cellranger so it could be a sample issue or a common feature of snRNA-seq samples.

Let’s separate our plots to look at single cell vs. single nucleus RNA-seq.

# first look at single nucleus samples only 
cell_qc_filter %>%
  filter(seq_unit == "nucleus") %>% 
  ggplot(aes(x = tool, y = sum, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/cell") + 
  xlab("") + 
  coord_cartesian(ylim = c(0,10000))

In general, it looks like SCPCR000118 has lower coverage than 119. Cellranger, Kallisto, and alevin-fry-unfiltered-sketch (with the pre mRNA index) actually seem to be have similar UMIs/cell in both samples, looking at the pre-mRNA index.

# single cell samples only 
cell_qc_filter %>%
  filter(seq_unit == "cell") %>% 
  ggplot(aes(x = tool, y = sum, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/cell") + 
  xlab("")

In general, these all look quite similar to each other. Alevin seems to be the outlier here and varies across samples. Kallisto is also on the slightly higher end than cellranger.

I think keeping the single cell and single nuclei samples separate is helpful for visualization so I’m going to keep them separate from here on out.

Now let’s look at genes/cell?

cell_qc_filter %>%
  filter(seq_unit == "nucleus") %>%
  ggplot(aes(x = tool, y = detected, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) + 
  theme_classic() + 
  ylab("Genes Detected/Cell") +
  theme(axis.ticks.x = element_blank(), axis.text.x = element_text(angle = 45, hjust = 1)) +
  ylim(c(0,6000))

Here, we see a bit more variation in genes/cell for the single nucleus samples than we did with the UMI/cell. It looks like kallisto and alevin-fry --unfiltered-pl with the --sketch mode looks comparable to cellranger. Alevin-fry-unfiltered-pl is on the higher end, and is consistently capturing more genes (not necessarily a good thing?). Alevin is showing a lot of variability in 119, but not 118.

# single cell samples only 
cell_qc_filter %>%
  filter(seq_unit == "cell") %>% 
  ggplot(aes(x = tool, y = detected, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes/cell") + 
  xlab("")

Here we are seeing similar trends in genes/cell as we did with UMI/cell for the single cell samples. We do see that alevin tends to report high genes/cell in all but 1 of the scRNA-seq samples. That same sample was the one that alevin also showed high mito content for, so perhaps that is why 006 is not as high for alevin. Cellranger is on the low end for all single cell samples, with alevin-fry --unfiltered-pl being mostly comparable.

Shared Cell QC, No Minimum Gene Coverage

What if we just look at cells that are found in all of the tools?

cell_only_qc_filter <- cell_qc_filter %>%
  filter(seq_unit == "cell")
nucleus_qc_filter <- cell_qc_filter %>%
  filter(seq_unit == "nucleus")
# filter for cells that are found in all 7 combinations of tools + index's
cell_counts <- cell_only_qc_filter %>%  
  dplyr::count(cell_id, sample)

common_cells <- cell_counts %>%
  dplyr::filter(n == 7) %>%
  dplyr::pull(cell_id)

cell_only_qc_common <- cell_only_qc_filter %>%
  dplyr::filter(
    (cell_id %in% common_cells) 
  )
# filter for cells that are found in 13 combinations of tools + index's for nucleus
nuclei_counts <- nucleus_qc_filter %>%
  dplyr::count(cell_id, sample)

common_nuclei <- nuclei_counts %>%
  dplyr::filter(n == 13) %>%
  dplyr::pull(cell_id)

nucleus_qc_common <- nucleus_qc_filter %>%
  dplyr::filter(
    (cell_id %in% common_nuclei)
  )

Now what happens to our mito content, UMI/cell, and genes/cell?

# mito comparison across shared cells only of all runs
# nucleus samples first 
ggplot(nucleus_qc_common, aes(x = tool, y = subsets_mito_percent, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) + 
  theme_classic() + 
  ylab("% Mito /Cell") + 
  theme(axis.ticks.x = element_blank(), axis.text.x = element_blank())


# single cell
ggplot(cell_only_qc_common, aes(x = tool, y = subsets_mito_percent, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) + 
  theme_classic() + 
  ylab("% Mito /Cell") + 
  theme(axis.ticks.x = element_blank(), axis.text.x = element_blank())

In looking at this plot, the mito content of SCPCR000118 is very high in the alevin tools, but not the other tools. The other samples show mostly consistent results across each sample and each tool.

# number of UMI's/sample in shared cells only 
# first look at single nucleus samples only 
ggplot(nucleus_qc_common, aes(x = tool, y = sum, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/cell") + 
  xlab("") + 
  ylim(c(0,20000))

Interestingly, when we look at shared cells only, we see an increase in coverage across the board. One thing to note, is that there is a drop off in number of shared cells from those found in 11 tools (~4000) to those found in 13 (~120).

Alevin-fry --sketch looks similar to cellranger. Alevin-fry --unfiltered-pl is also similar to cellranger and closer to kallisto having higher UMI/cell (in 119). In general, we see increased coverage using the pre mRNA index across all tools.

What about the single cell samples?

# number of UMI's/sample in shared cells only 
# scRNA seq samples
ggplot(cell_only_qc_common, aes(x = tool, y = sum, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/cell") + 
  xlab("")

So it looks like there is generally uniform coverage across all the samples in all tools.

Is that same pattern consistent in genes/cell in shared cells only?

# genes/cell in shared cells only
# single nucleus samples only 
ggplot(nucleus_qc_common, aes(x = tool, y = detected, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes/cell") + 
  xlab("")

Overall, the single nuclei samples have a lot more variation. Alevin-fry --sketch still seem to be the most similar to cellranger (with the pre mRNA index). Alevin-fry --unfiltered-pl and --sketch is higher than cellranger in both samples.

# single cell samples only 
ggplot(cell_only_qc_common, aes(x = tool, y = detected, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes/cell") + 
  xlab("")

Generally, the coverage is still quite uniform. However, it looks like cellranger has one of the lowest genes/cell means with alevin-fry --sketch and alevin-fry --unfiltered the highest.

Shared Cell QC, Minimum Gene Coverage > 5

Let’s do the same thing, but only look at genes above a certain threshold. Is alevin-fry still giving us the highest genes/cell across shared cells in single cell and kallisto/alevin-fry-unfiltered-sketch the highest in single nucleus?

perCellQCMetrics_threshold <- function(sce, mito = mito_genes, threshold){
  # add per cell QC with mitochondrial genes separately for later comparisons
  scater::perCellQCMetrics(
    sce,
    threshold = threshold
  )
}
alevin_QC_metrics <- alevin_sces %>% 
  purrr::map(perCellQCMetrics_threshold, threshold = 5)

alevin_fry_QC_metrics <- alevin_fry_sces %>%
  purrr::map(perCellQCMetrics_threshold, threshold = 5)

alevin_fry_sketch_QC_metrics <- alevin_fry_sketch_sces %>%
  purrr::map(perCellQCMetrics_threshold, threshold = 5)

alevin_fry_unfiltered_QC_metrics <- alevin_fry_unfiltered_sces %>%
  purrr::map(perCellQCMetrics_threshold, threshold = 5)

alevin_fry_unfiltered_sketch_QC_metrics <- alevin_fry_unfiltered_sketch_sces %>%
  purrr::map(perCellQCMetrics_threshold, threshold = 5)

kallisto_QC_metrics <- kallisto_sces %>% 
  purrr::map(perCellQCMetrics_threshold, threshold = 5)

cellranger_QC_metrics <- cellranger_sces %>% 
  purrr::map(perCellQCMetrics_threshold, threshold = 5)
## merge all QC output into one data frame to work with for plotting comparisons
QC_metrics_to_df <- function(QC_metrics){
  # move cell id to a column
  as.data.frame(QC_metrics) %>%
  tibble::rownames_to_column(var = "cell_id") %>%
    # add a column to get # of cells detected in that sample
    mutate(cells_detected = ncol(QC_metrics))
}


alevin_cell_qc_thresh_5 <- alevin_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

alevin_fry_cell_qc_thresh_5 <- alevin_fry_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

alevin_fry_sketch_cell_qc_thresh_5 <- alevin_fry_sketch_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

alevin_fry_unfiltered_cell_qc_thresh_5 <- alevin_fry_unfiltered_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

alevin_fry_unfiltered_sketch_cell_qc_thresh_5 <- alevin_fry_unfiltered_sketch_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

kallisto_cell_qc_thresh_5 <- kallisto_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

cellranger_cell_qc_thresh_5 <- cellranger_QC_metrics %>%
  purrr::map_df(QC_metrics_to_df, .id = "quant_id")

# combine all the data frames into one
cell_qc_thresh_5 <- dplyr::bind_rows(
  alevin = alevin_cell_qc_thresh_5,
  alevin_fry = alevin_fry_cell_qc_thresh_5,
  alevin_fry_sketch = alevin_fry_sketch_cell_qc_thresh_5,
  alevin_fry_unfiltered = alevin_fry_unfiltered_cell_qc_thresh_5, 
  alevin_fry_unfiltered_sketch = alevin_fry_unfiltered_sketch_cell_qc_thresh_5,
  kallisto = kallisto_cell_qc_thresh_5,
  cellranger = cellranger_cell_qc_thresh_5,
  .id = "tool"
) %>%
  dplyr::left_join(quant_info,
                   by = c("tool" = "tool", 
                          "quant_id" = "quant_dir")) %>%
  dplyr::filter(quant_id != "SCPCR000119-txome_k31") %>%
  dplyr::filter(quant_id != "SCPCR000118-spliced_intron_txome_k31-salign") %>%
  dplyr::filter(quant_id != "SCPCR000119-spliced_intron_txome_k31-salign")
#first remove SCPCR000003
cell_qc_thresh_5 <- cell_qc_thresh_5 %>%
  dplyr::filter(sample != "SCPCR000003")
cell_only_qc_thresh_5 <- cell_qc_thresh_5 %>%
  filter(seq_unit == "cell")
nucleus_qc_thresh_5 <- cell_qc_thresh_5 %>%
  filter(seq_unit == "nucleus")
# filter for cells that are found in all 7 combinations of tools + index's
cell_counts_thresh_5 <- cell_only_qc_thresh_5 %>%  
  dplyr::count(cell_id, sample)

common_cells_thresh_5 <- cell_counts_thresh_5 %>%
  dplyr::filter(n == 7) %>%
  dplyr::pull(cell_id)

cell_qc_common_thresh_5 <- cell_only_qc_thresh_5 %>%
  dplyr::filter(
    (cell_id %in% common_cells_thresh_5) 
  )
# filter for cells that are found in 13 combinations of tools + index's for nucleus
nuclei_counts_thresh_5 <- nucleus_qc_thresh_5 %>%
  dplyr::count(cell_id, sample)

common_nuclei_thresh_5 <- nuclei_counts_thresh_5 %>%
  dplyr::filter(n == 13) %>%
  dplyr::pull(cell_id)

nucleus_qc_common_thresh_5 <- nucleus_qc_thresh_5 %>%
  dplyr::filter(
    (cell_id %in% common_nuclei_thresh_5)
  )
# do we see a change in the number of genes/cell across tools if we remove low covered genes? 
# single nucleus samples
 ggplot(nucleus_qc_common_thresh_5, aes(x = tool, y = detected, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes/cell") + 
  xlab("") + 
  ylim(c(0,1500))

When we remove lowly covered genes in the single nuclei samples, the increased genes/cell seen in alevin-fry sketch-unfiltered over cellranger is still apparent. We also see kallisto with the pre mRNA index has the highest genes/cell in 119 only, but is not consistent.

# single cell samples
ggplot(cell_qc_common_thresh_5, aes(x = tool, y = detected, fill = tool)) + 
  geom_boxplot() + 
  facet_grid(sample ~ index_type) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes/cell") + 
  xlab("")

Some of the variation goes away when removing the lowly covered genes, but in general all tools give quite similar coverage across shared cells for single cell.

Although the total number of genes detected and the range decreases across all cells, it appears that lowly covered genes do not affect the distribution of genes/cell in the single cell samples but do for the single nuclei samples. This could very well be an effect of the lower capture seen in single nuclei samples.

Now that we’ve looked at the stats for the shared cells, let’s look and see how many cells are shared across which tools.

# spread table for comparisons
cell_qc_common_cor <- cell_only_qc_common %>%
  # spread the mean expression stats to one column per caller
  tidyr::pivot_wider(id_cols = c(cell_id, sample),
                     names_from = tool,
                     values_from = sum) %>%
  # drop rows with NA values to ease correlation calculations
  tidyr::drop_na()

nucleus_qc_common_cor <- nucleus_qc_common %>%
  tidyr::pivot_wider(id_cols = c(cell_id, sample),
                     names_from = c("tool", "index_type"),
                     values_from = sum) %>%
  tidyr::drop_na()
cell_qc_common_cor %>% 
  dplyr::group_by(sample) %>%
  dplyr::summarize(
    cr_al_cor = cor(cellranger, alevin, method = "spearman"),
    cr_al_fry_cor =cor(cellranger, alevin_fry, method = "spearman"),
    cr_al_fry_sketch_cor = cor(cellranger, alevin_fry_sketch, method = "spearman"),
    cr_al_fry_unfiltered_cor = cor(cellranger, alevin_fry_unfiltered, method = "spearman"), 
    cr_al_fry_unfiltered_sketch_cor = cor(cellranger, alevin_fry_unfiltered_sketch, method = "spearman"),
    cr_ka_cor = cor(cellranger, kallisto, method = "spearman")
  )

As expected based on the boxplots, the correlations are quite high between the UMIs/cells when looking at shared cells compared to cellranger. Kallisto appears to have the lowest correlations, with Alevin-fry and alevin-fry --unfiltered-pl the highest.

ggplot(cell_qc_common_cor, aes(x = cellranger, y = alevin_fry)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  facet_wrap(~ sample) + 
  scale_x_log10() + 
  scale_y_log10() + 
  labs(x = "Cell Ranger UMI/cell", y = "Alevin Fry UMI/cell") + 
  theme_classic()

# here we have to take into account the index names so let's only look at the pre_mRNA index comparisons to cellranger pre mRNA index for now
nucleus_qc_common_cor %>% 
  dplyr::group_by(sample) %>%
  dplyr::summarize(
    cr_al_cor = cor(`cellranger_pre-mRNA`, `alevin_pre-mRNA`, method = "spearman"),
    cr_al_fry_cor =cor(`cellranger_pre-mRNA`, `alevin_fry_pre-mRNA`, method = "spearman"),
    cr_al_fry_sketch_cor = cor(`cellranger_pre-mRNA`, `alevin_fry_pre-mRNA`, method = "spearman"),
    cr_al_fry_unfiltered_cor = cor(`cellranger_pre-mRNA`, `alevin_fry_unfiltered_cDNA`, method = "spearman"), 
    cr_al_fry_unfiltered_sketch_cor = cor(`cellranger_pre-mRNA`, `alevin_fry_unfiltered_sketch_pre-mRNA`, method = "spearman"),
    cr_ka_cor = cor(`cellranger_pre-mRNA`, `kallisto_pre-mRNA`, method = "spearman")
  )

Correlations with the single nuclei samples are much less consistent. Sample 118 is in general lower than 119 (potentially a problem with sample quality?). We also see that Kallisto, as like with single cell, doesn’t have as high of correlation as the other methods in 119, but higher in 118? The best correlation with cellranger in both samples looks like alevin-fry --unfiltered-pl --sketch.

Let’s look at one with high correlation and one with low correlation.

ggplot(nucleus_qc_common_cor, aes(x = `cellranger_pre-mRNA`, y = `alevin_fry_unfiltered_sketch_pre-mRNA`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  facet_wrap(~ sample) + 
  scale_x_log10() + 
  scale_y_log10() + 
  labs(x = "Cell Ranger UMI/cell", y = "Alevin Fry Unfiltered SKetch UMI/cell") + 
  theme_classic()

So now we can see why these correlations are so off, especially for 118, there are very few shared cell barcodes.

ggplot(nucleus_qc_common_cor, aes(x = `cellranger_pre-mRNA`, y = `kallisto_pre-mRNA`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  facet_wrap(~ sample) + 
  scale_x_log10() + 
  scale_y_log10() + 
  labs(x = "Cell Ranger UMI/cell", y = "Kallisto UMI/cell") + 
  theme_classic()

Here you can see that for 119, Kallisto has generally much higher UMI/cell, but higher variance than Alevin-Fry-Unfiltered-Sketch.

SessionInfo

sessionInfo()
R version 4.0.5 (2021-03-31)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] HDF5Array_1.18.1            rhdf5_2.34.0                DelayedArray_0.16.3         Matrix_1.3-3                ggforce_0.3.3               SingleCellExperiment_1.12.0
 [7] SummarizedExperiment_1.20.0 Biobase_2.50.0              GenomicRanges_1.42.0        GenomeInfoDb_1.26.7         IRanges_2.24.1              S4Vectors_0.28.1           
[13] BiocGenerics_0.36.1         MatrixGenerics_1.2.1        matrixStats_0.58.0          forcats_0.5.1               stringr_1.4.0               dplyr_1.0.6                
[19] purrr_0.3.4                 readr_1.4.0                 tidyr_1.1.3                 tibble_3.1.2                ggplot2_3.3.3               tidyverse_1.3.1            

loaded via a namespace (and not attached):
 [1] bitops_1.0-7              fs_1.5.0                  lubridate_1.7.10          httr_1.4.2                tools_4.0.5               backports_1.2.1          
 [7] utf8_1.2.1                R6_2.5.0                  irlba_2.3.3               vipor_0.4.5               DBI_1.1.1                 colorspace_2.0-1         
[13] rhdf5filters_1.2.1        withr_2.4.2               tidyselect_1.1.1          gridExtra_2.3             compiler_4.0.5            cli_2.5.0                
[19] rvest_1.0.0               BiocNeighbors_1.8.2       xml2_1.3.2                labeling_0.4.2            scales_1.1.1              digest_0.6.27            
[25] rmarkdown_2.8             XVector_0.30.0            scater_1.18.6             pkgconfig_2.0.3           htmltools_0.5.1.1         sparseMatrixStats_1.2.1  
[31] dbplyr_2.1.1              rlang_0.4.11              readxl_1.3.1              rstudioapi_0.13           DelayedMatrixStats_1.12.3 farver_2.1.0             
[37] generics_0.1.0            jsonlite_1.7.2            BiocParallel_1.24.1       RCurl_1.98-1.3            magrittr_2.0.1            BiocSingular_1.6.0       
[43] GenomeInfoDbData_1.2.4    scuttle_1.0.4             Rhdf5lib_1.12.1           Rcpp_1.0.6                ggbeeswarm_0.6.0          munsell_0.5.0            
[49] fansi_0.4.2               viridis_0.6.1             lifecycle_1.0.0           stringi_1.6.2             yaml_2.2.1                MASS_7.3-54              
[55] zlibbioc_1.36.0           grid_4.0.5                crayon_1.4.1              lattice_0.20-44           haven_2.4.1               beachmat_2.6.4           
[61] hms_1.1.0                 knitr_1.33                pillar_1.6.1              reprex_2.0.0              glue_1.4.2                evaluate_0.14            
[67] BiocManager_1.30.15       modelr_0.1.8              tweenr_1.0.2              vctrs_0.3.8               cellranger_1.1.0          polyclip_1.10-0          
[73] gtable_0.3.0              assertthat_0.2.1          xfun_0.23                 rsvd_1.0.5                broom_0.7.6               viridisLite_0.4.0        
[79] tinytex_0.31              beeswarm_0.3.1            ellipsis_0.3.2           
LS0tCnRpdGxlOiAiUHJlIFByb2Nlc3NpbmcgTWV0cmljcyBCZW5jaG1hcmtpbmciCmF1dGhvcjogIkFsbHkgSGF3a2lucyBmb3IgQ0NETCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpUaGlzIG5vdGVib29rIGlzIGEgZm9sbG93IHVwIG5vdGVib29rIHRvIHByZXZpb3VzIGJlbmNobWFya2luZyBkb25lIGluIGAwMi1jb21wYXJlLXF1YW50cy5STURgLiAKSGVyZSwgd2UgaGF2ZSB1c2VkIHRoZSBtb3N0IHJlY2VudCByZWxlYXNlIG9mIFtTYWxtb25dKGh0dHBzOi8vZ2l0aHViLmNvbS9DT01CSU5FLWxhYi9zYWxtb24vcmVsZWFzZXMvdGFnL3YxLjQuMCkgYW5kIFtDZWxscmFuZ2VyIDYuMC4wXShodHRwczovL3N1cHBvcnQuMTB4Z2Vub21pY3MuY29tL3NpbmdsZS1jZWxsLWdlbmUtZXhwcmVzc2lvbi9zb2Z0d2FyZS9waXBlbGluZXMvbGF0ZXN0L3JlbGVhc2Utbm90ZXMpLCBhbmQgaW5jbHVkZWQgdGhlIG5ldyBbQWxldmluLUZyeV0oaHR0cHM6Ly9hbGV2aW4tZnJ5LnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC9nZXR0aW5nX3N0YXJ0ZWQuaHRtbCkgbWV0aG9kLiAKQWxldmluLUZyeSBjYW4gdXNlIGVpdGhlciBhIHNlbGVjdGl2ZSBhbGlnbm1lbnQgc3RyYXRlZ3kgb3IgcHNldWRvYWxpZ25tZW50IHN0cmF0ZWd5ICh3aXRoIHRoZSBgLS1za2V0Y2hgIGZsYWcpLHNvIGhlcmUgd2UgYXJlIHRlc3RpbmcgYm90aCBzdHJhdGVnaWVzLiAKQWxldmluLUZyeSBhbHNvIGNhbiB1c2UgdGhlIDEwWCB3aGl0ZWxpc3QgZm9yIGJhcmNvZGUgYXNzaWdubWVudCByYXRoZXIgdGhhbiBnZW5lcmF0aW5nIHRoZSBwdXRhdGl2ZSB3aGl0ZWxpc3QgdXNpbmcgdGhlIGAtLXVuZmlsdGVyZWQtcGxgIGZsYWcsIHNvIHdlIGhhdmUgYWxzbyBpbmNsdWRlZCB0aGF0IGluIG91ciB0ZXN0cy4KClRoZSByZXNvbHV0aW9uIGN1cnJlbnRseSB1c2VkIGZvciBBbGV2aW4tRnJ5IGlzIHRoZSBGdWxsIHJlc29sdXRpb24uIFNlZSB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBkaWZmZXJlbnQgb3B0aW9ucyBmb3IgcmVzb2x1dGlvbiB0aGF0IGNvdWxkIGJlIHVzZWQuIAoKVGhlIGdvYWwgb2YgdGhpcyBub3RlYm9vayBpcyB0byBjb21wYXJlIHRoZSBxdWFudGlmaWNhdGlvbiBwZXJmb3JtZWQgYnkgZWFjaCB0b29sIGFjcm9zcyB0aGUgc2FtcGxlcyB3ZSBoYXZlIGN1cnJlbnRseSBydW4uIApUaHVzIGZhciwgd2UgaGF2ZSA0IDEwWHYzIHNpbmdsZSBjZWxsIFJOQS1zZXEgc2FtcGxlcyB0aGF0IGhhdmUgYmVlbiBydW4gb24gYWxsIHRvb2xzLCBTQ1BDUjAwMDAwMywgU0NQQ1IwMDAwMDYsIFNDUENSMDAwMTI2LCBhbmQgU0NQQ1IwMDAxMjcuIApXZSBhbHNvIGhhdmUgMiAxMFh2My4xIHNpbmdsZSBudWNsZXVzIFJOQS1zZXEgc2FtcGxlcyB0aGF0IGhhdmUgYmVlbiBydW4gb24gYWxsIHRvb2xzLCBTQ1BDUjAwMDExOCBhbmQgU0NQQ1IwMDAxMTkuCkFsbCB0aGUgc25STkEtc2VxIHNhbXBsZXMgaGF2ZSBiZWVuIHJ1biB1c2luZyBlaXRoZXIgdGhlIHByZS1tUk5BIGluZGV4IG9yIHRoZSBtUk5BIGluZGV4LiAKT25lIHRoaW5nIHRvIG5vdGUgaXMgdGhhdCB0aGUgbmV3IHNwbGljZWQgaW5kZXggaXMgZGlmZmVyZW50IGZyb20gdGhlIHByZXZpb3VzbHkgdXNlZCBpbmRleCdzIHdpdGggdGhlIHNpbmdsZSBjZWxsIFJOQSBzZXEgc2FtcGxlcyBpbiB0aGF0IGl0IHdhcyBjcmVhdGVkIGJ5IGdyYWJiaW5nIHRoZSBnZW5vbWljIHJlZ2lvbnMgY29ycmVzcG9uZGluZyB0byBzcGxpY2VkIGNETkEgb25seSBmcm9tIHRoZSBlbnNlbWJsIGd0ZiwgcmF0aGVyIHRoYW4gdGFraW5nIHRoZSBjRE5BLmZhc3RhIGRpcmVjdGx5IGZyb20gZW5zZW1ibC4gCgojIyBTZXR1cCAKCiMjIyBMb2FkIExpYnJhcmllcyBmb3IgaW1wb3J0CgpgYGB7ciBzZXR1cH0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShnZ2ZvcmNlKQpgYGAKCiMjIyBGaWxlIGFuZCBEaXJlY3RvcnkgU2V0dXAKCmBgYHtyfQojIHdoZXJlIGFsbCB0aGUgZGF0YSBsaXZlcyBhZnRlciBydW5uaW5nIDAxLWltcG9ydC1xdWFudC1kYXRhLlJtZAppbXBvcnRfZGlyIDwtIGZpbGUucGF0aCgnZGF0YScpCgpxdWFudF9pbmZvX2ZpbGUgPC0gZmlsZS5wYXRoKGltcG9ydF9kaXIsICdxdWFudF9pbmZvLnRzdicpCgphbGV2aW5fcmRzIDwtIGZpbGUucGF0aChpbXBvcnRfZGlyLCAnYWxldmluX3NjZXMucmRzJykKYWxldmluX2ZyeV9yZHMgPC0gZmlsZS5wYXRoKGltcG9ydF9kaXIsICdhbGV2aW5fZnJ5X3NjZXMucmRzJykKYWxldmluX2ZyeV9za2V0Y2hfcmRzIDwtIGZpbGUucGF0aChpbXBvcnRfZGlyLCAnYWxldmluX2ZyeV9za2V0Y2hfc2Nlcy5yZHMnKQphbGV2aW5fZnJ5X3VuZmlsdGVyZWRfcmRzIDwtIGZpbGUucGF0aChpbXBvcnRfZGlyLCAnYWxldmluX2ZyeV91bmZpbHRlcmVkX3NjZXMucmRzJykKYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9yZHMgPC0gZmlsZS5wYXRoKGltcG9ydF9kaXIsICdhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX3NjZXMucmRzJykKa2FsbGlzdG9fcmRzIDwtIGZpbGUucGF0aChpbXBvcnRfZGlyLCAna2FsbGlzdG9fc2Nlcy5yZHMnKQpjZWxscmFuZ2VyX3JkcyA8LSBmaWxlLnBhdGgoaW1wb3J0X2RpciwgJ2NlbGxyYW5nZXJfc2Nlcy5yZHMnKQpgYGAKCgpgYGB7cn0KIyByZWFkIGluIHF1YW50IGluZm8gZmlsZSBjcmVhdGVkIGluIDAxLWltcG9ydC1xdWFudC1kYXRhLlJtZApxdWFudF9pbmZvIDwtIHJlYWRyOjpyZWFkX3RzdihxdWFudF9pbmZvX2ZpbGUpCgojIG1ha2Ugc3VyZSB0aGF0IHRoZSB0b29sIGlzIGxhYmVsZWQgYXMgYWxldmluX2ZyeV9za2V0Y2gvIHVuZmlsdGVyZWQgdG8gZGlzdGluZ3Vpc2ggZnJvbSByZWd1bGFyIGFsZXZpbl9mcnkgZm9yIGxhdGVyCnF1YW50X2luZm9bd2hpY2gocXVhbnRfaW5mbyRhbGV2aW5fYWxpZ25tZW50ID09ICJza2V0Y2giICYgcXVhbnRfaW5mbyRhbGV2aW5fcGVybWl0X2xpc3QgIT0gInVuZmlsdGVyZWQiKSwidG9vbCJdIDwtICJhbGV2aW5fZnJ5X3NrZXRjaCIKcXVhbnRfaW5mb1t3aGljaChxdWFudF9pbmZvJGFsZXZpbl9hbGlnbm1lbnQgPT0gInNhbGlnbiIgJiBxdWFudF9pbmZvJGFsZXZpbl9wZXJtaXRfbGlzdCA9PSAidW5maWx0ZXJlZCIpLCAidG9vbCJdIDwtICJhbGV2aW5fZnJ5X3VuZmlsdGVyZWQiCnF1YW50X2luZm9bd2hpY2gocXVhbnRfaW5mbyRhbGV2aW5fYWxpZ25tZW50ID09ICJza2V0Y2giICYgcXVhbnRfaW5mbyRhbGV2aW5fcGVybWl0X2xpc3QgPT0gInVuZmlsdGVyZWQiKSwgInRvb2wiXSA8LSAiYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaCIKCiMgc2F2ZSBjaGFuZ2VzIHRvIHF1YW50IGZpbGUKcXVhbnRfaW5mb19maWxlX3VwZGF0ZSA8LSBmaWxlLnBhdGgoaW1wb3J0X2RpciwgJ3F1YW50X2luZm9fdXBkYXRlLnRzdicpCnJlYWRyOjp3cml0ZV90c3YocXVhbnRfaW5mbywgZmlsZS5wYXRoKHF1YW50X2luZm9fZmlsZV91cGRhdGUpKQpgYGAKCgpgYGB7cn0KIyBFYWNoIFJEUyBmaWxlIGNvbnRhaW5zIGEgU2luZ2xlQ2VsbEV4cGVyaW1lbnQKYWxldmluX3NjZXMgPC0gcmVhZHI6OnJlYWRfcmRzKGFsZXZpbl9yZHMpCmFsZXZpbl9mcnlfc2NlcyA8LSByZWFkcjo6cmVhZF9yZHMoYWxldmluX2ZyeV9yZHMpCmFsZXZpbl9mcnlfc2tldGNoX3NjZXMgPC0gcmVhZHI6OnJlYWRfcmRzKGFsZXZpbl9mcnlfc2tldGNoX3JkcykKYWxldmluX2ZyeV91bmZpbHRlcmVkX3NjZXMgPC0gcmVhZHI6OnJlYWRfcmRzKGFsZXZpbl9mcnlfdW5maWx0ZXJlZF9yZHMpCmFsZXZpbl9mcnlfdW5maWx0ZXJlZF9za2V0Y2hfc2NlcyA8LSByZWFkcjo6cmVhZF9yZHMoYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9yZHMpCmthbGxpc3RvX3NjZXMgPC0gcmVhZHI6OnJlYWRfcmRzKGthbGxpc3RvX3JkcykKY2VsbHJhbmdlcl9zY2VzIDwtIHJlYWRyOjpyZWFkX3JkcyhjZWxscmFuZ2VyX3JkcykKYGBgCgoKYGBge3J9CiMjIGdldCBhbGwgYW5ub3RhdGlvbiBkYXRhIGZyb20gUzMKYW5ub3RhdGlvbl9kaXIgPC0gZmlsZS5wYXRoKGltcG9ydF9kaXIsICJhbm5vdGF0aW9uIikKCiMjIGdldCBtaXRvY2hvbmRyaWFsIGdlbmUgbGlzdCBmcm9tIHMzCmFubm90YXRpb25fZmlsZXNfczMgPC0gCiAgJ3MzOi8vbmV4dGZsb3ctY2NkbC1kYXRhL3JlZmVyZW5jZS9ob21vX3NhcGllbnMvZW5zZW1ibC0xMDAvYW5ub3RhdGlvbi8nCgpzeW5jX2NhbGwgPC0gcGFzdGUoJ2F3cyBzMyBzeW5jJywgYW5ub3RhdGlvbl9maWxlc19zMywgYW5ub3RhdGlvbl9kaXIpCnN5c3RlbShzeW5jX2NhbGwsIGlnbm9yZS5zdGRvdXQgPSBUUlVFKQpgYGAKCmBgYHtyfQptaXRvX2ZpbGUgPC0gZmlsZS5wYXRoKGFubm90YXRpb25fZGlyLCAiSG9tb19zYXBpZW5zLmVuc2VtYmwuMTAwLm1pdG9nZW5lcy50eHQiKQoKbWl0b19nZW5lcyA8LSByZWFkcjo6cmVhZF90c3YobWl0b19maWxlLCBjb2xfbmFtZXMgPSAiZ2VuZV9pZCIpCm1pdG9fZ2VuZXMgPC0gbWl0b19nZW5lcyAlPiUKICBwdWxsKGdlbmVfaWQpCmBgYAoKCiMjIEFkZCBRQyBNZXRyaWNzCgpgYGB7cn0KYWRkUGVyQ2VsbFFDX21pdG8gPC0gZnVuY3Rpb24oc2NlLCBtaXRvID0gbWl0b19nZW5lcyl7CiAgIyBhZGQgcGVyIGNlbGwgUUMgd2l0aCBtaXRvY2hvbmRyaWFsIGdlbmVzIHNlcGFyYXRlbHkgZm9yIGxhdGVyIGNvbXBhcmlzb25zCiAgc2NhdGVyOjphZGRQZXJDZWxsUUMoCiAgICBzY2UsIAogICAgc3Vic2V0cyA9IGxpc3QobWl0byA9IG1pdG9bbWl0byAlaW4lIHJvd25hbWVzKHNjZSldKQogICkKfQpgYGAKCmBgYHtyfQphbGV2aW5fc2NlcyA8LSBhbGV2aW5fc2NlcyAlPiUgCiAgcHVycnI6Om1hcChhZGRQZXJDZWxsUUNfbWl0bykKCmFsZXZpbl9mcnlfc2NlcyA8LSBhbGV2aW5fZnJ5X3NjZXMgJT4lCiAgcHVycnI6Om1hcChhZGRQZXJDZWxsUUNfbWl0bykKCmFsZXZpbl9mcnlfc2tldGNoX3NjZXMgPC0gYWxldmluX2ZyeV9za2V0Y2hfc2NlcyAlPiUKICBwdXJycjo6bWFwKGFkZFBlckNlbGxRQ19taXRvKQoKYWxldmluX2ZyeV91bmZpbHRlcmVkX3NjZXMgPC0gYWxldmluX2ZyeV91bmZpbHRlcmVkX3NjZXMgJT4lCiAgcHVycnI6Om1hcChhZGRQZXJDZWxsUUNfbWl0bykKCmFsZXZpbl9mcnlfdW5maWx0ZXJlZF9za2V0Y2hfc2NlcyA8LSBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX3NjZXMgJT4lCiAgcHVycnI6Om1hcChhZGRQZXJDZWxsUUNfbWl0bykKCmthbGxpc3RvX3NjZXMgPC0ga2FsbGlzdG9fc2NlcyAlPiUgCiAgcHVycnI6Om1hcChhZGRQZXJDZWxsUUNfbWl0bykKCmNlbGxyYW5nZXJfc2NlcyA8LSBjZWxscmFuZ2VyX3NjZXMgJT4lIAogIHB1cnJyOjptYXAoYWRkUGVyQ2VsbFFDX21pdG8pCgpgYGAKCgpgYGB7cn0KIyMgbWVyZ2UgYWxsIFFDIG91dHB1dCBpbnRvIG9uZSBkYXRhIGZyYW1lIHRvIHdvcmsgd2l0aCBmb3IgcGxvdHRpbmcgY29tcGFyaXNvbnMKY2VsbGRhdGFfdG9fZGYgPC0gZnVuY3Rpb24oc2NlKXsKICAjIGV4dHJhY3QgdGhlIGNvbHVtbiAoY2VsbCkgc3VtbWFyeSBkYXRhIGZyb20gYSBTQ0UsIAogICMgY29udmVydCB0byBkYXRhIGZyYW1lIGFuZCBtb3ZlIGNlbGwgaWQgdG8gYSBjb2x1bW4KICBhcy5kYXRhLmZyYW1lKGNvbERhdGEoc2NlKSkgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gImNlbGxfaWQiKSAlPiUKICAgICMgYWRkIGEgY29sdW1uIHRvIGdldCAjIG9mIGNlbGxzIGRldGVjdGVkIGluIHRoYXQgc2FtcGxlCiAgICBtdXRhdGUoY2VsbHNfZGV0ZWN0ZWQgPSBuY29sKHNjZSkpCn0KCmFsZXZpbl9jZWxsX3FjIDwtIGFsZXZpbl9zY2VzICU+JQogIHB1cnJyOjptYXBfZGYoY2VsbGRhdGFfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgphbGV2aW5fZnJ5X2NlbGxfcWMgPC0gYWxldmluX2ZyeV9zY2VzICU+JQogIHB1cnJyOjptYXBfZGYoY2VsbGRhdGFfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgphbGV2aW5fZnJ5X3NrZXRjaF9jZWxsX3FjIDwtIGFsZXZpbl9mcnlfc2tldGNoX3NjZXMgJT4lCiAgcHVycnI6Om1hcF9kZihjZWxsZGF0YV90b19kZiwgLmlkID0gInF1YW50X2lkIikKCmFsZXZpbl9mcnlfdW5maWx0ZXJlZF9jZWxsX3FjIDwtIGFsZXZpbl9mcnlfdW5maWx0ZXJlZF9zY2VzICU+JQogIHB1cnJyOjptYXBfZGYoY2VsbGRhdGFfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgphbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX2NlbGxfcWMgPC0gYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9zY2VzICU+JQogIHB1cnJyOjptYXBfZGYoY2VsbGRhdGFfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgprYWxsaXN0b19jZWxsX3FjIDwtIGthbGxpc3RvX3NjZXMgJT4lCiAgcHVycnI6Om1hcF9kZihjZWxsZGF0YV90b19kZiwgLmlkID0gInF1YW50X2lkIikKCmNlbGxyYW5nZXJfY2VsbF9xYyA8LSBjZWxscmFuZ2VyX3NjZXMgJT4lCiAgcHVycnI6Om1hcF9kZihjZWxsZGF0YV90b19kZiwgLmlkID0gInF1YW50X2lkIikKCiMgY29tYmluZSBhbGwgdGhlIGRhdGEgZnJhbWVzIGludG8gb25lCmNlbGxfcWMgPC0gZHBseXI6OmJpbmRfcm93cygKICBhbGV2aW4gPSBhbGV2aW5fY2VsbF9xYywKICBhbGV2aW5fZnJ5ID0gYWxldmluX2ZyeV9jZWxsX3FjLAogIGFsZXZpbl9mcnlfc2tldGNoID0gYWxldmluX2ZyeV9za2V0Y2hfY2VsbF9xYywKICBhbGV2aW5fZnJ5X3VuZmlsdGVyZWQgPSBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfY2VsbF9xYywKICBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoID0gYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9jZWxsX3FjLAogIGthbGxpc3RvID0ga2FsbGlzdG9fY2VsbF9xYywKICBjZWxscmFuZ2VyID0gY2VsbHJhbmdlcl9jZWxsX3FjLAogIC5pZCA9ICJ0b29sIgopICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4ocXVhbnRfaW5mbywKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidG9vbCIgPSAidG9vbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJxdWFudF9pZCIgPSAicXVhbnRfZGlyIikpICU+JQogICMgcmVtb3ZlIGV4dHJhbmVvdXMgcnVuIGZyb20gMTE5IHdpdGggdHhvbWUgaW5kZXggc2luY2Ugd2UgaGF2ZSB0aGUgcnVuIHdpdGggdGhlIHNwbGljZWQgaW5kZXgKICBkcGx5cjo6ZmlsdGVyKHF1YW50X2lkICE9ICJTQ1BDUjAwMDExOS10eG9tZV9rMzEiKSAlPiUKICAjIHJlbW92ZSBkdXBsaWNhdGUgcnVucyBvZiBhbGV2aW4tZnJ5IC0ta25lZSBvbiAxMTgsIDExOQogIGRwbHlyOjpmaWx0ZXIocXVhbnRfaWQgIT0iU0NQQ1IwMDAxMTgtc3BsaWNlZF9pbnRyb25fdHhvbWVfazMxLXNhbGlnbiIpICU+JQogIGRwbHlyOjpmaWx0ZXIocXVhbnRfaWQgIT0gIlNDUENSMDAwMTE5LXNwbGljZWRfaW50cm9uX3R4b21lX2szMS1zYWxpZ24iKQogICAgICAgICAgICAgICAgICAKYGBgCgoKIyMgQ29tcGFyaXNvbiBvZiBRQyBNZXRyaWNzIGFjcm9zcyBhbGwgdG9vbHMKCiMjIyBQZXIgQ2VsbCBRQwoKTGV0J3MgZmlyc3Qgc3RhcnQgYnkgdGFraW5nIGEgbG9vayBhdCBzb21lIGdlbmVyYWwgaW5mb3JtYXRpb24gYWNyb3NzIGFsbCBvZiBvdXIgdG9vbHMgYW5kIGhvdyBjb25zaXN0ZW50IGVhY2ggb2YgdGhlc2UgbWV0cmljcyBpcyBmb3IgYWxsIGZvdXIgb2YgdGhlIHNhbXBsZXMuIEhlcmUgYXJlIHRoZSBtZXRyaWNzIHdlIHdhbnQgdG8gbG9vayBhdDogCgogICogVG90YWwgbnVtYmVyIG9mIGNlbGxzIGRldGVjdGVkCiAgKiBNaXRvY2hvbmRyaWFsIGNvbnRlbnQvIGNlbGwKICAqIE51bWJlciBvZiBVTUlzL2NlbGwKICAqIE51bWJlciBvZiBHZW5lcy9jZWxsIAoKCmBgYHtyfQojIyBtYWtlIG5ldyBkYXRhIGZyYW1lIHdpdGggbWVhbiBvZiBjZWxscyBkZXRlY3RlZCBhbmQgc3RhbmRhcmQgZGV2CmNlbGxzX2RldGVjdGVkX3N0YXRzIDwtIGNlbGxfcWMgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlLCB0b29sKSAlPiUKICBzdW1tYXJpc2UoY2VsbHNfZGV0ZWN0ZWQgPSBtZWFuKGNlbGxzX2RldGVjdGVkKSkKY2VsbHNfZGV0ZWN0ZWRfc3RhdHMKCiMjIGZpcnN0IGxvb2sgYXQgY2VsbHMgZGV0ZWN0ZWQKZ2dwbG90KGNlbGxzX2RldGVjdGVkX3N0YXRzLCBhZXMoeCA9IHRvb2wsIHkgPSBjZWxsc19kZXRlY3RlZCkpICsKICBnZW9tX2ppdHRlcihtYXBwaW5nID0gYWVzKGNvbG9yID0gc2FtcGxlKSkgKwogIGdlb21fdmlvbGluKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgeWxhYigiVG90YWwgQ2VsbHMgRGV0ZWN0ZWQiKSArIAogIHhsYWIoIiIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCkJhc2VkIG9uIHRoaXMsIGl0IGxvb2tzIGxpa2UgYWxldmluLWZyeSB1c2luZyB0aGUgYC0ta25lZS1kaXN0YW5jZWAgZG9lcyBub3QgaGFuZGxlIHNpbmdsZS1udWNsZWkgc2FtcGxlcyB3ZWxsLiAKSW4gY29udHJhc3QsIHRoZSBhbGV2aW4tZnJ5IGAtLXVuZmlsdGVyZWQtcGxgIGFuZCBrYWxsaXN0byBzYW1wbGVzIHdlcmUgZmlsdGVyZWQgdXNpbmcgYERyb3BsZXRVdGlsczo6ZW1wdHlEcm9wc2AgYW5kIHRoZXkgaGF2ZSBtdWNoIGxvd2VyIG51bWJlcnMgZm9yIHRoZSBzYW1lIHNpbmdsZS1udWNsZWkgc2FtcGxlcy4gCgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgbWl0b2Nob25kcmlhbCBjb250ZW50IHBlciBjZWxsIGFjcm9zcyB0aGUgdG9vbHMgYW5kIHNlZSBpZiB3ZSBzZWUgdmFyaWF0aW9uIHRoZXJlLiAKCgpgYGB7ciBmaWcuaGVpZ2h0ID0gMTAsIGZpZy53aWR0aCA9IDEwfQpnZ3Bsb3QoY2VsbF9xYywgYWVzKHggPSB0b29sLCB5ID0gc3Vic2V0c19taXRvX3BlcmNlbnQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKyAKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB5bGFiKCIlIE1pdG8gL0NlbGwiKSArIAogIHRoZW1lKGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCkl0IGxvb2tzIGxpa2UgU0NQQ1IwMDAwMDMgaXMgYSB2ZXJ5IHBvb3Igc2FtcGxlIGFjcm9zcyBhbGwgdG9vbHMgYW5kIGhhcyBhIGxvdCBvZiBkZWFkIGNlbGxzLiAKSXQgbWlnaHQgYmUgYSBnb29kIGlkZWEgdG8gcmVtb3ZlIHRoYXQgZnJvbSBvdXIgY29tcGFyaXNvbnMgYW5kIG5vdCBjb25zaWRlciB0aGlzIGZvciBiZW5jaG1hcmtpbmcuIApBZGRpdGlvbmFsbHksIGl0IGxvb2tzIGxpa2Ugd2UgY2FuIHNlZSB0aGF0IFNDUENSMDAwMTE4IChzblJOQXNlcSkgYWxzbyBoYXMgc29tZSB2YXJpYXRpb24sIGJ1dCB0aGUgb3RoZXIgc2FtcGxlcyB0ZW5kIHRvIGJlIGZhaXJseSBjb25zaXN0ZW50LiAKCgpgYGB7cn0KIyByZW1vdmUgU0NQQ1IwMDAwMDMgZnJvbSBjZWxsX3FjIGZvciB0aGUgcmVzdCBvZiB0aGUgcGxvdHRpbmcgCmNlbGxfcWNfZmlsdGVyIDwtIGNlbGxfcWMgJT4lCiAgZHBseXI6OmZpbHRlcihzYW1wbGUgIT0gIlNDUENSMDAwMDAzIikKYGBgCgpOb3cgbGV0J3MgbG9vayBhdCB0aGUgbnVtYmVyIG9mIFVNSSdzIGRldGVjdGVkL2NlbGwgdGhhdCB3ZSd2ZSByZW1vdmVkIG91ciBvdXRsaWVyIHNhbXBsZS4gCgpgYGB7ciBmaWcuaGVpZ2h0ID0gMTAsIGZpZy53aWR0aCA9IDEwfQojIG51bWJlciBvZiBVTUkncy9zYW1wbGUKZ2dwbG90KGNlbGxfcWNfZmlsdGVyLCBhZXMoeCA9IHRvb2wsIHkgPSBzdW0sIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiVU1JL2NlbGwiKSArIAogIHhsYWIoIiIpCmBgYApBdCBmaXJzdCBnbGFuY2UsIFNDUENSMDAwMTE4IGFuZCBTQ1BDUjAwMDExOSAodGhlIHNpbmdsZSBudWNsZXVzIFJOQS1zZXEgc2FtcGxlcykgaGF2ZSB2ZXJ5IGxvdyB2YWx1ZXMgb2YgVU1JL2NlbGwuIApJcyB0aGlzIGJlY2F1c2UgdGhlc2Ugc2FtcGxlcyBhcmUgcG9vciBxdWFsaXR5PyAKT3IgaXMgaXQgYSBwcm9ibGVtIHdpdGggdGhlIGluZGV4PyAKSG93ZXZlciwgdGhlIHNhbXBsZXMgZG9uJ3QgbG9vayBhbnkgYmV0dGVyIHdpdGggY2VsbHJhbmdlciBzbyBpdCBjb3VsZCBiZSBhIHNhbXBsZSBpc3N1ZSBvciBhIGNvbW1vbiBmZWF0dXJlIG9mIHNuUk5BLXNlcSBzYW1wbGVzLiAKCkxldCdzIHNlcGFyYXRlIG91ciBwbG90cyB0byBsb29rIGF0IHNpbmdsZSBjZWxsIHZzLiBzaW5nbGUgbnVjbGV1cyBSTkEtc2VxLgoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CiMgZmlyc3QgbG9vayBhdCBzaW5nbGUgbnVjbGV1cyBzYW1wbGVzIG9ubHkgCmNlbGxfcWNfZmlsdGVyICU+JQogIGZpbHRlcihzZXFfdW5pdCA9PSAibnVjbGV1cyIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB0b29sLCB5ID0gc3VtLCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKHNhbXBsZSB+IGluZGV4X3R5cGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIlVNSS9jZWxsIikgKyAKICB4bGFiKCIiKSArIAogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDEwMDAwKSkKYGBgCkluIGdlbmVyYWwsIGl0IGxvb2tzIGxpa2UgU0NQQ1IwMDAxMTggaGFzIGxvd2VyIGNvdmVyYWdlIHRoYW4gMTE5LiAKQ2VsbHJhbmdlciwgS2FsbGlzdG8sIGFuZCBhbGV2aW4tZnJ5LXVuZmlsdGVyZWQtc2tldGNoICh3aXRoIHRoZSBwcmUgbVJOQSBpbmRleCkgYWN0dWFsbHkgc2VlbSB0byBiZSBoYXZlIHNpbWlsYXIgVU1Jcy9jZWxsIGluIGJvdGggc2FtcGxlcywgbG9va2luZyBhdCB0aGUgcHJlLW1STkEgaW5kZXguIAoKCmBgYHtyIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGg9MTB9CiMgc2luZ2xlIGNlbGwgc2FtcGxlcyBvbmx5IApjZWxsX3FjX2ZpbHRlciAlPiUKICBmaWx0ZXIoc2VxX3VuaXQgPT0gImNlbGwiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdG9vbCwgeSA9IHN1bSwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZChzYW1wbGUgfiBpbmRleF90eXBlKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAKICB5bGFiKCJVTUkvY2VsbCIpICsgCiAgeGxhYigiIikKYGBgCkluIGdlbmVyYWwsIHRoZXNlIGFsbCBsb29rIHF1aXRlIHNpbWlsYXIgdG8gZWFjaCBvdGhlci4gQWxldmluIHNlZW1zIHRvIGJlIHRoZSBvdXRsaWVyIGhlcmUgYW5kIHZhcmllcyBhY3Jvc3Mgc2FtcGxlcy4gCkthbGxpc3RvIGlzIGFsc28gb24gdGhlIHNsaWdodGx5IGhpZ2hlciBlbmQgdGhhbiBjZWxscmFuZ2VyLgoKSSB0aGluayBrZWVwaW5nIHRoZSBzaW5nbGUgY2VsbCBhbmQgc2luZ2xlIG51Y2xlaSBzYW1wbGVzIHNlcGFyYXRlIGlzIGhlbHBmdWwgZm9yIHZpc3VhbGl6YXRpb24gc28gSSdtIGdvaW5nIHRvIGtlZXAgdGhlbSBzZXBhcmF0ZSBmcm9tIGhlcmUgb24gb3V0LiAKCk5vdyBsZXQncyBsb29rIGF0IGdlbmVzL2NlbGw/IAoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CmNlbGxfcWNfZmlsdGVyICU+JQogIGZpbHRlcihzZXFfdW5pdCA9PSAibnVjbGV1cyIpICU+JQogIGdncGxvdChhZXMoeCA9IHRvb2wsIHkgPSBkZXRlY3RlZCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgZmFjZXRfZ3JpZChzYW1wbGUgfiBpbmRleF90eXBlKSArIAogIHRoZW1lX2NsYXNzaWMoKSArIAogIHlsYWIoIkdlbmVzIERldGVjdGVkL0NlbGwiKSArCiAgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgeWxpbShjKDAsNjAwMCkpCmBgYApIZXJlLCB3ZSBzZWUgYSBiaXQgbW9yZSB2YXJpYXRpb24gaW4gZ2VuZXMvY2VsbCBmb3IgdGhlIHNpbmdsZSBudWNsZXVzIHNhbXBsZXMgdGhhbiB3ZSBkaWQgd2l0aCB0aGUgVU1JL2NlbGwuIApJdCBsb29rcyBsaWtlIGthbGxpc3RvIGFuZCBhbGV2aW4tZnJ5IGAtLXVuZmlsdGVyZWQtcGxgIHdpdGggdGhlIGAtLXNrZXRjaGAgbW9kZSBsb29rcyBjb21wYXJhYmxlIHRvIGNlbGxyYW5nZXIuIApBbGV2aW4tZnJ5LXVuZmlsdGVyZWQtcGwgaXMgb24gdGhlIGhpZ2hlciBlbmQsIGFuZCBpcyBjb25zaXN0ZW50bHkgY2FwdHVyaW5nIG1vcmUgZ2VuZXMgKG5vdCBuZWNlc3NhcmlseSBhIGdvb2QgdGhpbmc/KS4gCkFsZXZpbiBpcyBzaG93aW5nIGEgbG90IG9mIHZhcmlhYmlsaXR5IGluIDExOSwgYnV0IG5vdCAxMTguIAoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGggPSAxMH0KIyBzaW5nbGUgY2VsbCBzYW1wbGVzIG9ubHkgCmNlbGxfcWNfZmlsdGVyICU+JQogIGZpbHRlcihzZXFfdW5pdCA9PSAiY2VsbCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB0b29sLCB5ID0gZGV0ZWN0ZWQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMvY2VsbCIpICsgCiAgeGxhYigiIikKYGBgCkhlcmUgd2UgYXJlIHNlZWluZyBzaW1pbGFyIHRyZW5kcyBpbiBnZW5lcy9jZWxsIGFzIHdlIGRpZCB3aXRoIFVNSS9jZWxsIGZvciB0aGUgc2luZ2xlIGNlbGwgc2FtcGxlcy4gCldlIGRvIHNlZSB0aGF0IGFsZXZpbiB0ZW5kcyB0byByZXBvcnQgaGlnaCBnZW5lcy9jZWxsIGluIGFsbCBidXQgMSBvZiB0aGUgc2NSTkEtc2VxIHNhbXBsZXMuIApUaGF0IHNhbWUgc2FtcGxlIHdhcyB0aGUgb25lIHRoYXQgYWxldmluIGFsc28gc2hvd2VkIGhpZ2ggbWl0byBjb250ZW50IGZvciwgc28gcGVyaGFwcyB0aGF0IGlzIHdoeSAwMDYgaXMgbm90IGFzIGhpZ2ggZm9yIGFsZXZpbi4KQ2VsbHJhbmdlciBpcyBvbiB0aGUgbG93IGVuZCBmb3IgYWxsIHNpbmdsZSBjZWxsIHNhbXBsZXMsIHdpdGggYWxldmluLWZyeSBgLS11bmZpbHRlcmVkLXBsYCBiZWluZyBtb3N0bHkgY29tcGFyYWJsZS4gCgojIyMgU2hhcmVkIENlbGwgUUMsIE5vIE1pbmltdW0gR2VuZSBDb3ZlcmFnZQoKV2hhdCBpZiB3ZSBqdXN0IGxvb2sgYXQgY2VsbHMgdGhhdCBhcmUgZm91bmQgaW4gYWxsIG9mIHRoZSB0b29scz8gCgpgYGB7cn0KY2VsbF9vbmx5X3FjX2ZpbHRlciA8LSBjZWxsX3FjX2ZpbHRlciAlPiUKICBmaWx0ZXIoc2VxX3VuaXQgPT0gImNlbGwiKQpudWNsZXVzX3FjX2ZpbHRlciA8LSBjZWxsX3FjX2ZpbHRlciAlPiUKICBmaWx0ZXIoc2VxX3VuaXQgPT0gIm51Y2xldXMiKQpgYGAKCgpgYGB7cn0KIyBmaWx0ZXIgZm9yIGNlbGxzIHRoYXQgYXJlIGZvdW5kIGluIGFsbCA3IGNvbWJpbmF0aW9ucyBvZiB0b29scyArIGluZGV4J3MKY2VsbF9jb3VudHMgPC0gY2VsbF9vbmx5X3FjX2ZpbHRlciAlPiUgIAogIGRwbHlyOjpjb3VudChjZWxsX2lkLCBzYW1wbGUpCgpjb21tb25fY2VsbHMgPC0gY2VsbF9jb3VudHMgJT4lCiAgZHBseXI6OmZpbHRlcihuID09IDcpICU+JQogIGRwbHlyOjpwdWxsKGNlbGxfaWQpCgpjZWxsX29ubHlfcWNfY29tbW9uIDwtIGNlbGxfb25seV9xY19maWx0ZXIgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIChjZWxsX2lkICVpbiUgY29tbW9uX2NlbGxzKSAKICApCmBgYAoKCmBgYHtyfQojIGZpbHRlciBmb3IgY2VsbHMgdGhhdCBhcmUgZm91bmQgaW4gMTMgY29tYmluYXRpb25zIG9mIHRvb2xzICsgaW5kZXgncyBmb3IgbnVjbGV1cwpudWNsZWlfY291bnRzIDwtIG51Y2xldXNfcWNfZmlsdGVyICU+JQogIGRwbHlyOjpjb3VudChjZWxsX2lkLCBzYW1wbGUpCgpjb21tb25fbnVjbGVpIDwtIG51Y2xlaV9jb3VudHMgJT4lCiAgZHBseXI6OmZpbHRlcihuID09IDEzKSAlPiUKICBkcGx5cjo6cHVsbChjZWxsX2lkKQoKbnVjbGV1c19xY19jb21tb24gPC0gbnVjbGV1c19xY19maWx0ZXIgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIChjZWxsX2lkICVpbiUgY29tbW9uX251Y2xlaSkKICApCmBgYAoKCgpOb3cgd2hhdCBoYXBwZW5zIHRvIG91ciBtaXRvIGNvbnRlbnQsIFVNSS9jZWxsLCBhbmQgZ2VuZXMvY2VsbD8gCgpgYGB7ciBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0xMH0KIyBtaXRvIGNvbXBhcmlzb24gYWNyb3NzIHNoYXJlZCBjZWxscyBvbmx5IG9mIGFsbCBydW5zCiMgbnVjbGV1cyBzYW1wbGVzIGZpcnN0IApnZ3Bsb3QobnVjbGV1c19xY19jb21tb24sIGFlcyh4ID0gdG9vbCwgeSA9IHN1YnNldHNfbWl0b19wZXJjZW50LCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKHNhbXBsZSB+IGluZGV4X3R5cGUpICsgCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgeWxhYigiJSBNaXRvIC9DZWxsIikgKyAKICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQoKIyBzaW5nbGUgY2VsbApnZ3Bsb3QoY2VsbF9vbmx5X3FjX2NvbW1vbiwgYWVzKHggPSB0b29sLCB5ID0gc3Vic2V0c19taXRvX3BlcmNlbnQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKyAKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB5bGFiKCIlIE1pdG8gL0NlbGwiKSArIAogIHRoZW1lKGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYApJbiBsb29raW5nIGF0IHRoaXMgcGxvdCwgdGhlIG1pdG8gY29udGVudCBvZiBTQ1BDUjAwMDExOCBpcyB2ZXJ5IGhpZ2ggaW4gdGhlIGFsZXZpbiB0b29scywgYnV0IG5vdCB0aGUgb3RoZXIgdG9vbHMuIApUaGUgb3RoZXIgc2FtcGxlcyBzaG93IG1vc3RseSBjb25zaXN0ZW50IHJlc3VsdHMgYWNyb3NzIGVhY2ggc2FtcGxlIGFuZCBlYWNoIHRvb2wuIAoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CiMgbnVtYmVyIG9mIFVNSSdzL3NhbXBsZSBpbiBzaGFyZWQgY2VsbHMgb25seSAKIyBmaXJzdCBsb29rIGF0IHNpbmdsZSBudWNsZXVzIHNhbXBsZXMgb25seSAKZ2dwbG90KG51Y2xldXNfcWNfY29tbW9uLCBhZXMoeCA9IHRvb2wsIHkgPSBzdW0sIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiVU1JL2NlbGwiKSArIAogIHhsYWIoIiIpICsgCiAgeWxpbShjKDAsMjAwMDApKQpgYGAKSW50ZXJlc3RpbmdseSwgd2hlbiB3ZSBsb29rIGF0IHNoYXJlZCBjZWxscyBvbmx5LCB3ZSBzZWUgYW4gaW5jcmVhc2UgaW4gY292ZXJhZ2UgYWNyb3NzIHRoZSBib2FyZC4gCk9uZSB0aGluZyB0byBub3RlLCBpcyB0aGF0IHRoZXJlIGlzIGEgZHJvcCBvZmYgaW4gbnVtYmVyIG9mIHNoYXJlZCBjZWxscyBmcm9tIHRob3NlIGZvdW5kIGluIDExIHRvb2xzICh+NDAwMCkgdG8gdGhvc2UgZm91bmQgaW4gMTMgKH4xMjApLgoKQWxldmluLWZyeSBgLS1za2V0Y2hgIGxvb2tzIHNpbWlsYXIgdG8gY2VsbHJhbmdlci4gCkFsZXZpbi1mcnkgYC0tdW5maWx0ZXJlZC1wbGAgaXMgYWxzbyBzaW1pbGFyIHRvIGNlbGxyYW5nZXIgYW5kIGNsb3NlciB0byBrYWxsaXN0byBoYXZpbmcgaGlnaGVyIFVNSS9jZWxsIChpbiAxMTkpLiAKSW4gZ2VuZXJhbCwgd2Ugc2VlIGluY3JlYXNlZCBjb3ZlcmFnZSB1c2luZyB0aGUgcHJlIG1STkEgaW5kZXggYWNyb3NzIGFsbCB0b29scy4KCldoYXQgYWJvdXQgdGhlIHNpbmdsZSBjZWxsIHNhbXBsZXM/IAoKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CiMgbnVtYmVyIG9mIFVNSSdzL3NhbXBsZSBpbiBzaGFyZWQgY2VsbHMgb25seSAKIyBzY1JOQSBzZXEgc2FtcGxlcwpnZ3Bsb3QoY2VsbF9vbmx5X3FjX2NvbW1vbiwgYWVzKHggPSB0b29sLCB5ID0gc3VtLCBmaWxsID0gdG9vbCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBmYWNldF9ncmlkKHNhbXBsZSB+IGluZGV4X3R5cGUpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIlVNSS9jZWxsIikgKyAKICB4bGFiKCIiKQoKYGBgClNvIGl0IGxvb2tzIGxpa2UgdGhlcmUgaXMgZ2VuZXJhbGx5IHVuaWZvcm0gY292ZXJhZ2UgYWNyb3NzIGFsbCB0aGUgc2FtcGxlcyBpbiBhbGwgdG9vbHMuICAKCklzIHRoYXQgc2FtZSBwYXR0ZXJuIGNvbnNpc3RlbnQgaW4gZ2VuZXMvY2VsbCBpbiBzaGFyZWQgY2VsbHMgb25seT8gCgpgYGB7ciBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoPTEwfQojIGdlbmVzL2NlbGwgaW4gc2hhcmVkIGNlbGxzIG9ubHkKIyBzaW5nbGUgbnVjbGV1cyBzYW1wbGVzIG9ubHkgCmdncGxvdChudWNsZXVzX3FjX2NvbW1vbiwgYWVzKHggPSB0b29sLCB5ID0gZGV0ZWN0ZWQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMvY2VsbCIpICsgCiAgeGxhYigiIikKCmBgYApPdmVyYWxsLCB0aGUgc2luZ2xlIG51Y2xlaSBzYW1wbGVzIGhhdmUgYSBsb3QgbW9yZSB2YXJpYXRpb24uIApBbGV2aW4tZnJ5IGAtLXNrZXRjaGAgc3RpbGwgc2VlbSB0byBiZSB0aGUgbW9zdCBzaW1pbGFyIHRvIGNlbGxyYW5nZXIgKHdpdGggdGhlIHByZSBtUk5BIGluZGV4KS4KQWxldmluLWZyeSBgLS11bmZpbHRlcmVkLXBsYCBhbmQgYC0tc2tldGNoYCBpcyBoaWdoZXIgdGhhbiBjZWxscmFuZ2VyIGluIGJvdGggc2FtcGxlcy4gCgpgYGB7ciBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gMTB9CiMgc2luZ2xlIGNlbGwgc2FtcGxlcyBvbmx5IApnZ3Bsb3QoY2VsbF9vbmx5X3FjX2NvbW1vbiwgYWVzKHggPSB0b29sLCB5ID0gZGV0ZWN0ZWQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMvY2VsbCIpICsgCiAgeGxhYigiIikKCmBgYApHZW5lcmFsbHksIHRoZSBjb3ZlcmFnZSBpcyBzdGlsbCBxdWl0ZSB1bmlmb3JtLiAKSG93ZXZlciwgaXQgbG9va3MgbGlrZSBjZWxscmFuZ2VyIGhhcyBvbmUgb2YgdGhlIGxvd2VzdCBnZW5lcy9jZWxsIG1lYW5zIHdpdGggYWxldmluLWZyeSBgLS1za2V0Y2hgIGFuZCBhbGV2aW4tZnJ5IGAtLXVuZmlsdGVyZWRgIHRoZSBoaWdoZXN0LiAKCiMjIyBTaGFyZWQgQ2VsbCBRQywgTWluaW11bSBHZW5lIENvdmVyYWdlID4gNQoKTGV0J3MgZG8gdGhlIHNhbWUgdGhpbmcsIGJ1dCBvbmx5IGxvb2sgYXQgZ2VuZXMgYWJvdmUgYSBjZXJ0YWluIHRocmVzaG9sZC4KSXMgYWxldmluLWZyeSBzdGlsbCBnaXZpbmcgdXMgdGhlIGhpZ2hlc3QgZ2VuZXMvY2VsbCBhY3Jvc3Mgc2hhcmVkIGNlbGxzIGluIHNpbmdsZSBjZWxsIGFuZCBrYWxsaXN0by9hbGV2aW4tZnJ5LXVuZmlsdGVyZWQtc2tldGNoIHRoZSBoaWdoZXN0IGluIHNpbmdsZSBudWNsZXVzPwoKYGBge3J9CnBlckNlbGxRQ01ldHJpY3NfdGhyZXNob2xkIDwtIGZ1bmN0aW9uKHNjZSwgbWl0byA9IG1pdG9fZ2VuZXMsIHRocmVzaG9sZCl7CiAgIyBhZGQgcGVyIGNlbGwgUUMgd2l0aCBtaXRvY2hvbmRyaWFsIGdlbmVzIHNlcGFyYXRlbHkgZm9yIGxhdGVyIGNvbXBhcmlzb25zCiAgc2NhdGVyOjpwZXJDZWxsUUNNZXRyaWNzKAogICAgc2NlLAogICAgdGhyZXNob2xkID0gdGhyZXNob2xkCiAgKQp9CmBgYAoKCmBgYHtyfQphbGV2aW5fUUNfbWV0cmljcyA8LSBhbGV2aW5fc2NlcyAlPiUgCiAgcHVycnI6Om1hcChwZXJDZWxsUUNNZXRyaWNzX3RocmVzaG9sZCwgdGhyZXNob2xkID0gNSkKCmFsZXZpbl9mcnlfUUNfbWV0cmljcyA8LSBhbGV2aW5fZnJ5X3NjZXMgJT4lCiAgcHVycnI6Om1hcChwZXJDZWxsUUNNZXRyaWNzX3RocmVzaG9sZCwgdGhyZXNob2xkID0gNSkKCmFsZXZpbl9mcnlfc2tldGNoX1FDX21ldHJpY3MgPC0gYWxldmluX2ZyeV9za2V0Y2hfc2NlcyAlPiUKICBwdXJycjo6bWFwKHBlckNlbGxRQ01ldHJpY3NfdGhyZXNob2xkLCB0aHJlc2hvbGQgPSA1KQoKYWxldmluX2ZyeV91bmZpbHRlcmVkX1FDX21ldHJpY3MgPC0gYWxldmluX2ZyeV91bmZpbHRlcmVkX3NjZXMgJT4lCiAgcHVycnI6Om1hcChwZXJDZWxsUUNNZXRyaWNzX3RocmVzaG9sZCwgdGhyZXNob2xkID0gNSkKCmFsZXZpbl9mcnlfdW5maWx0ZXJlZF9za2V0Y2hfUUNfbWV0cmljcyA8LSBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX3NjZXMgJT4lCiAgcHVycnI6Om1hcChwZXJDZWxsUUNNZXRyaWNzX3RocmVzaG9sZCwgdGhyZXNob2xkID0gNSkKCmthbGxpc3RvX1FDX21ldHJpY3MgPC0ga2FsbGlzdG9fc2NlcyAlPiUgCiAgcHVycnI6Om1hcChwZXJDZWxsUUNNZXRyaWNzX3RocmVzaG9sZCwgdGhyZXNob2xkID0gNSkKCmNlbGxyYW5nZXJfUUNfbWV0cmljcyA8LSBjZWxscmFuZ2VyX3NjZXMgJT4lIAogIHB1cnJyOjptYXAocGVyQ2VsbFFDTWV0cmljc190aHJlc2hvbGQsIHRocmVzaG9sZCA9IDUpCgpgYGAKCgpgYGB7cn0KIyMgbWVyZ2UgYWxsIFFDIG91dHB1dCBpbnRvIG9uZSBkYXRhIGZyYW1lIHRvIHdvcmsgd2l0aCBmb3IgcGxvdHRpbmcgY29tcGFyaXNvbnMKUUNfbWV0cmljc190b19kZiA8LSBmdW5jdGlvbihRQ19tZXRyaWNzKXsKICAjIG1vdmUgY2VsbCBpZCB0byBhIGNvbHVtbgogIGFzLmRhdGEuZnJhbWUoUUNfbWV0cmljcykgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gImNlbGxfaWQiKSAlPiUKICAgICMgYWRkIGEgY29sdW1uIHRvIGdldCAjIG9mIGNlbGxzIGRldGVjdGVkIGluIHRoYXQgc2FtcGxlCiAgICBtdXRhdGUoY2VsbHNfZGV0ZWN0ZWQgPSBuY29sKFFDX21ldHJpY3MpKQp9CgoKYWxldmluX2NlbGxfcWNfdGhyZXNoXzUgPC0gYWxldmluX1FDX21ldHJpY3MgJT4lCiAgcHVycnI6Om1hcF9kZihRQ19tZXRyaWNzX3RvX2RmLCAuaWQgPSAicXVhbnRfaWQiKQoKYWxldmluX2ZyeV9jZWxsX3FjX3RocmVzaF81IDwtIGFsZXZpbl9mcnlfUUNfbWV0cmljcyAlPiUKICBwdXJycjo6bWFwX2RmKFFDX21ldHJpY3NfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgphbGV2aW5fZnJ5X3NrZXRjaF9jZWxsX3FjX3RocmVzaF81IDwtIGFsZXZpbl9mcnlfc2tldGNoX1FDX21ldHJpY3MgJT4lCiAgcHVycnI6Om1hcF9kZihRQ19tZXRyaWNzX3RvX2RmLCAuaWQgPSAicXVhbnRfaWQiKQoKYWxldmluX2ZyeV91bmZpbHRlcmVkX2NlbGxfcWNfdGhyZXNoXzUgPC0gYWxldmluX2ZyeV91bmZpbHRlcmVkX1FDX21ldHJpY3MgJT4lCiAgcHVycnI6Om1hcF9kZihRQ19tZXRyaWNzX3RvX2RmLCAuaWQgPSAicXVhbnRfaWQiKQoKYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9jZWxsX3FjX3RocmVzaF81IDwtIGFsZXZpbl9mcnlfdW5maWx0ZXJlZF9za2V0Y2hfUUNfbWV0cmljcyAlPiUKICBwdXJycjo6bWFwX2RmKFFDX21ldHJpY3NfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgprYWxsaXN0b19jZWxsX3FjX3RocmVzaF81IDwtIGthbGxpc3RvX1FDX21ldHJpY3MgJT4lCiAgcHVycnI6Om1hcF9kZihRQ19tZXRyaWNzX3RvX2RmLCAuaWQgPSAicXVhbnRfaWQiKQoKY2VsbHJhbmdlcl9jZWxsX3FjX3RocmVzaF81IDwtIGNlbGxyYW5nZXJfUUNfbWV0cmljcyAlPiUKICBwdXJycjo6bWFwX2RmKFFDX21ldHJpY3NfdG9fZGYsIC5pZCA9ICJxdWFudF9pZCIpCgojIGNvbWJpbmUgYWxsIHRoZSBkYXRhIGZyYW1lcyBpbnRvIG9uZQpjZWxsX3FjX3RocmVzaF81IDwtIGRwbHlyOjpiaW5kX3Jvd3MoCiAgYWxldmluID0gYWxldmluX2NlbGxfcWNfdGhyZXNoXzUsCiAgYWxldmluX2ZyeSA9IGFsZXZpbl9mcnlfY2VsbF9xY190aHJlc2hfNSwKICBhbGV2aW5fZnJ5X3NrZXRjaCA9IGFsZXZpbl9mcnlfc2tldGNoX2NlbGxfcWNfdGhyZXNoXzUsCiAgYWxldmluX2ZyeV91bmZpbHRlcmVkID0gYWxldmluX2ZyeV91bmZpbHRlcmVkX2NlbGxfcWNfdGhyZXNoXzUsIAogIGFsZXZpbl9mcnlfdW5maWx0ZXJlZF9za2V0Y2ggPSBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX2NlbGxfcWNfdGhyZXNoXzUsCiAga2FsbGlzdG8gPSBrYWxsaXN0b19jZWxsX3FjX3RocmVzaF81LAogIGNlbGxyYW5nZXIgPSBjZWxscmFuZ2VyX2NlbGxfcWNfdGhyZXNoXzUsCiAgLmlkID0gInRvb2wiCikgJT4lCiAgZHBseXI6OmxlZnRfam9pbihxdWFudF9pbmZvLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIiA9ICJ0b29sIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgInF1YW50X2lkIiA9ICJxdWFudF9kaXIiKSkgJT4lCiAgZHBseXI6OmZpbHRlcihxdWFudF9pZCAhPSAiU0NQQ1IwMDAxMTktdHhvbWVfazMxIikgJT4lCiAgZHBseXI6OmZpbHRlcihxdWFudF9pZCAhPSAiU0NQQ1IwMDAxMTgtc3BsaWNlZF9pbnRyb25fdHhvbWVfazMxLXNhbGlnbiIpICU+JQogIGRwbHlyOjpmaWx0ZXIocXVhbnRfaWQgIT0gIlNDUENSMDAwMTE5LXNwbGljZWRfaW50cm9uX3R4b21lX2szMS1zYWxpZ24iKQpgYGAKCgoKYGBge3J9CiNmaXJzdCByZW1vdmUgU0NQQ1IwMDAwMDMKY2VsbF9xY190aHJlc2hfNSA8LSBjZWxsX3FjX3RocmVzaF81ICU+JQogIGRwbHlyOjpmaWx0ZXIoc2FtcGxlICE9ICJTQ1BDUjAwMDAwMyIpCmBgYAoKCmBgYHtyfQpjZWxsX29ubHlfcWNfdGhyZXNoXzUgPC0gY2VsbF9xY190aHJlc2hfNSAlPiUKICBmaWx0ZXIoc2VxX3VuaXQgPT0gImNlbGwiKQpudWNsZXVzX3FjX3RocmVzaF81IDwtIGNlbGxfcWNfdGhyZXNoXzUgJT4lCiAgZmlsdGVyKHNlcV91bml0ID09ICJudWNsZXVzIikKYGBgCgoKYGBge3J9CiMgZmlsdGVyIGZvciBjZWxscyB0aGF0IGFyZSBmb3VuZCBpbiBhbGwgNyBjb21iaW5hdGlvbnMgb2YgdG9vbHMgKyBpbmRleCdzCmNlbGxfY291bnRzX3RocmVzaF81IDwtIGNlbGxfb25seV9xY190aHJlc2hfNSAlPiUgIAogIGRwbHlyOjpjb3VudChjZWxsX2lkLCBzYW1wbGUpCgpjb21tb25fY2VsbHNfdGhyZXNoXzUgPC0gY2VsbF9jb3VudHNfdGhyZXNoXzUgJT4lCiAgZHBseXI6OmZpbHRlcihuID09IDcpICU+JQogIGRwbHlyOjpwdWxsKGNlbGxfaWQpCgpjZWxsX3FjX2NvbW1vbl90aHJlc2hfNSA8LSBjZWxsX29ubHlfcWNfdGhyZXNoXzUgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIChjZWxsX2lkICVpbiUgY29tbW9uX2NlbGxzX3RocmVzaF81KSAKICApCgpgYGAKCgoKYGBge3J9CiMgZmlsdGVyIGZvciBjZWxscyB0aGF0IGFyZSBmb3VuZCBpbiAxMyBjb21iaW5hdGlvbnMgb2YgdG9vbHMgKyBpbmRleCdzIGZvciBudWNsZXVzCm51Y2xlaV9jb3VudHNfdGhyZXNoXzUgPC0gbnVjbGV1c19xY190aHJlc2hfNSAlPiUKICBkcGx5cjo6Y291bnQoY2VsbF9pZCwgc2FtcGxlKQoKY29tbW9uX251Y2xlaV90aHJlc2hfNSA8LSBudWNsZWlfY291bnRzX3RocmVzaF81ICU+JQogIGRwbHlyOjpmaWx0ZXIobiA9PSAxMykgJT4lCiAgZHBseXI6OnB1bGwoY2VsbF9pZCkKCm51Y2xldXNfcWNfY29tbW9uX3RocmVzaF81IDwtIG51Y2xldXNfcWNfdGhyZXNoXzUgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIChjZWxsX2lkICVpbiUgY29tbW9uX251Y2xlaV90aHJlc2hfNSkKICApCgpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KIyBkbyB3ZSBzZWUgYSBjaGFuZ2UgaW4gdGhlIG51bWJlciBvZiBnZW5lcy9jZWxsIGFjcm9zcyB0b29scyBpZiB3ZSByZW1vdmUgbG93IGNvdmVyZWQgZ2VuZXM/IAojIHNpbmdsZSBudWNsZXVzIHNhbXBsZXMKIGdncGxvdChudWNsZXVzX3FjX2NvbW1vbl90aHJlc2hfNSwgYWVzKHggPSB0b29sLCB5ID0gZGV0ZWN0ZWQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMvY2VsbCIpICsgCiAgeGxhYigiIikgKyAKICB5bGltKGMoMCwxNTAwKSkKYGBgCldoZW4gd2UgcmVtb3ZlIGxvd2x5IGNvdmVyZWQgZ2VuZXMgaW4gdGhlIHNpbmdsZSBudWNsZWkgc2FtcGxlcywgdGhlIGluY3JlYXNlZCBnZW5lcy9jZWxsIHNlZW4gaW4gYWxldmluLWZyeSBza2V0Y2gtdW5maWx0ZXJlZCBvdmVyIGNlbGxyYW5nZXIgaXMgc3RpbGwgYXBwYXJlbnQuIApXZSBhbHNvIHNlZSBrYWxsaXN0byB3aXRoIHRoZSBwcmUgbVJOQSBpbmRleCBoYXMgdGhlIGhpZ2hlc3QgZ2VuZXMvY2VsbCBpbiAxMTkgb25seSwgYnV0IGlzIG5vdCBjb25zaXN0ZW50LiAKCmBgYHtyIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSAxMH0KIyBzaW5nbGUgY2VsbCBzYW1wbGVzCmdncGxvdChjZWxsX3FjX2NvbW1vbl90aHJlc2hfNSwgYWVzKHggPSB0b29sLCB5ID0gZGV0ZWN0ZWQsIGZpbGwgPSB0b29sKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogIGZhY2V0X2dyaWQoc2FtcGxlIH4gaW5kZXhfdHlwZSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiR2VuZXMvY2VsbCIpICsgCiAgeGxhYigiIikKYGBgClNvbWUgb2YgdGhlIHZhcmlhdGlvbiBnb2VzIGF3YXkgd2hlbiByZW1vdmluZyB0aGUgbG93bHkgY292ZXJlZCBnZW5lcywgYnV0IGluIGdlbmVyYWwgYWxsIHRvb2xzIGdpdmUgcXVpdGUgc2ltaWxhciBjb3ZlcmFnZSBhY3Jvc3Mgc2hhcmVkIGNlbGxzIGZvciBzaW5nbGUgY2VsbC4KCkFsdGhvdWdoIHRoZSB0b3RhbCBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgYW5kIHRoZSByYW5nZSBkZWNyZWFzZXMgYWNyb3NzIGFsbCBjZWxscywgaXQgYXBwZWFycyB0aGF0IGxvd2x5IGNvdmVyZWQgZ2VuZXMgZG8gbm90IGFmZmVjdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGdlbmVzL2NlbGwgaW4gdGhlIHNpbmdsZSBjZWxsIHNhbXBsZXMgYnV0IGRvIGZvciB0aGUgc2luZ2xlIG51Y2xlaSBzYW1wbGVzLiAKVGhpcyBjb3VsZCB2ZXJ5IHdlbGwgYmUgYW4gZWZmZWN0IG9mIHRoZSBsb3dlciBjYXB0dXJlIHNlZW4gaW4gc2luZ2xlIG51Y2xlaSBzYW1wbGVzLiAKCk5vdyB0aGF0IHdlJ3ZlIGxvb2tlZCBhdCB0aGUgc3RhdHMgZm9yIHRoZSBzaGFyZWQgY2VsbHMsIGxldCdzIGxvb2sgYW5kIHNlZSBob3cgbWFueSBjZWxscyBhcmUgc2hhcmVkIGFjcm9zcyB3aGljaCB0b29scy4gIAoKYGBge3J9CiMgc3ByZWFkIHRhYmxlIGZvciBjb21wYXJpc29ucwpjZWxsX3FjX2NvbW1vbl9jb3IgPC0gY2VsbF9vbmx5X3FjX2NvbW1vbiAlPiUKICAjIHNwcmVhZCB0aGUgbWVhbiBleHByZXNzaW9uIHN0YXRzIHRvIG9uZSBjb2x1bW4gcGVyIGNhbGxlcgogIHRpZHlyOjpwaXZvdF93aWRlcihpZF9jb2xzID0gYyhjZWxsX2lkLCBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBuYW1lc19mcm9tID0gdG9vbCwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBzdW0pICU+JQogICMgZHJvcCByb3dzIHdpdGggTkEgdmFsdWVzIHRvIGVhc2UgY29ycmVsYXRpb24gY2FsY3VsYXRpb25zCiAgdGlkeXI6OmRyb3BfbmEoKQoKbnVjbGV1c19xY19jb21tb25fY29yIDwtIG51Y2xldXNfcWNfY29tbW9uICU+JQogIHRpZHlyOjpwaXZvdF93aWRlcihpZF9jb2xzID0gYyhjZWxsX2lkLCBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBuYW1lc19mcm9tID0gYygidG9vbCIsICJpbmRleF90eXBlIiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gc3VtKSAlPiUKICB0aWR5cjo6ZHJvcF9uYSgpCmBgYAoKCmBgYHtyfQpjZWxsX3FjX2NvbW1vbl9jb3IgJT4lIAogIGRwbHlyOjpncm91cF9ieShzYW1wbGUpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBjcl9hbF9jb3IgPSBjb3IoY2VsbHJhbmdlciwgYWxldmluLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIGNyX2FsX2ZyeV9jb3IgPWNvcihjZWxscmFuZ2VyLCBhbGV2aW5fZnJ5LCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIGNyX2FsX2ZyeV9za2V0Y2hfY29yID0gY29yKGNlbGxyYW5nZXIsIGFsZXZpbl9mcnlfc2tldGNoLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgIGNyX2FsX2ZyeV91bmZpbHRlcmVkX2NvciA9IGNvcihjZWxscmFuZ2VyLCBhbGV2aW5fZnJ5X3VuZmlsdGVyZWQsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLCAKICAgIGNyX2FsX2ZyeV91bmZpbHRlcmVkX3NrZXRjaF9jb3IgPSBjb3IoY2VsbHJhbmdlciwgYWxldmluX2ZyeV91bmZpbHRlcmVkX3NrZXRjaCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBjcl9rYV9jb3IgPSBjb3IoY2VsbHJhbmdlciwga2FsbGlzdG8sIG1ldGhvZCA9ICJzcGVhcm1hbiIpCiAgKQpgYGAKQXMgZXhwZWN0ZWQgYmFzZWQgb24gdGhlIGJveHBsb3RzLCB0aGUgY29ycmVsYXRpb25zIGFyZSBxdWl0ZSBoaWdoIGJldHdlZW4gdGhlIFVNSXMvY2VsbHMgd2hlbiBsb29raW5nIGF0IHNoYXJlZCBjZWxscyBjb21wYXJlZCB0byBjZWxscmFuZ2VyLiAKS2FsbGlzdG8gYXBwZWFycyB0byBoYXZlIHRoZSBsb3dlc3QgY29ycmVsYXRpb25zLCB3aXRoIEFsZXZpbi1mcnkgYW5kIGFsZXZpbi1mcnkgYC0tdW5maWx0ZXJlZC1wbGAgdGhlIGhpZ2hlc3QuIAoKYGBge3J9CmdncGxvdChjZWxsX3FjX2NvbW1vbl9jb3IsIGFlcyh4ID0gY2VsbHJhbmdlciwgeSA9IGFsZXZpbl9mcnkpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBVTUkvY2VsbCIsIHkgPSAiQWxldmluIEZyeSBVTUkvY2VsbCIpICsgCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CiMgaGVyZSB3ZSBoYXZlIHRvIHRha2UgaW50byBhY2NvdW50IHRoZSBpbmRleCBuYW1lcyBzbyBsZXQncyBvbmx5IGxvb2sgYXQgdGhlIHByZV9tUk5BIGluZGV4IGNvbXBhcmlzb25zIHRvIGNlbGxyYW5nZXIgcHJlIG1STkEgaW5kZXggZm9yIG5vdwpudWNsZXVzX3FjX2NvbW1vbl9jb3IgJT4lIAogIGRwbHlyOjpncm91cF9ieShzYW1wbGUpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBjcl9hbF9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfcHJlLW1STkFgLCBgYWxldmluX3ByZS1tUk5BYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBjcl9hbF9mcnlfY29yID1jb3IoYGNlbGxyYW5nZXJfcHJlLW1STkFgLCBgYWxldmluX2ZyeV9wcmUtbVJOQWAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgY3JfYWxfZnJ5X3NrZXRjaF9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfcHJlLW1STkFgLCBgYWxldmluX2ZyeV9wcmUtbVJOQWAsIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgY3JfYWxfZnJ5X3VuZmlsdGVyZWRfY29yID0gY29yKGBjZWxscmFuZ2VyX3ByZS1tUk5BYCwgYGFsZXZpbl9mcnlfdW5maWx0ZXJlZF9jRE5BYCwgbWV0aG9kID0gInNwZWFybWFuIiksIAogICAgY3JfYWxfZnJ5X3VuZmlsdGVyZWRfc2tldGNoX2NvciA9IGNvcihgY2VsbHJhbmdlcl9wcmUtbVJOQWAsIGBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX3ByZS1tUk5BYCwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICBjcl9rYV9jb3IgPSBjb3IoYGNlbGxyYW5nZXJfcHJlLW1STkFgLCBga2FsbGlzdG9fcHJlLW1STkFgLCBtZXRob2QgPSAic3BlYXJtYW4iKQogICkKYGBgCkNvcnJlbGF0aW9ucyB3aXRoIHRoZSBzaW5nbGUgbnVjbGVpIHNhbXBsZXMgYXJlIG11Y2ggbGVzcyBjb25zaXN0ZW50LiBTYW1wbGUgMTE4IGlzIGluIGdlbmVyYWwgbG93ZXIgdGhhbiAxMTkgKHBvdGVudGlhbGx5IGEgcHJvYmxlbSB3aXRoIHNhbXBsZSBxdWFsaXR5PykuIApXZSBhbHNvIHNlZSB0aGF0IEthbGxpc3RvLCBhcyBsaWtlIHdpdGggc2luZ2xlIGNlbGwsIGRvZXNuJ3QgaGF2ZSBhcyBoaWdoIG9mIGNvcnJlbGF0aW9uIGFzIHRoZSBvdGhlciBtZXRob2RzIGluIDExOSwgYnV0IGhpZ2hlciBpbiAxMTg/IApUaGUgYmVzdCBjb3JyZWxhdGlvbiB3aXRoIGNlbGxyYW5nZXIgaW4gYm90aCBzYW1wbGVzIGxvb2tzIGxpa2UgYWxldmluLWZyeSBgLS11bmZpbHRlcmVkLXBsYCBgLS1za2V0Y2hgLiAKCkxldCdzIGxvb2sgYXQgb25lIHdpdGggaGlnaCBjb3JyZWxhdGlvbiBhbmQgb25lIHdpdGggbG93IGNvcnJlbGF0aW9uLiAKCmBgYHtyfQpnZ3Bsb3QobnVjbGV1c19xY19jb21tb25fY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX3ByZS1tUk5BYCwgeSA9IGBhbGV2aW5fZnJ5X3VuZmlsdGVyZWRfc2tldGNoX3ByZS1tUk5BYCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC4xKSArIAogIGZhY2V0X3dyYXAofiBzYW1wbGUpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsgCiAgbGFicyh4ID0gIkNlbGwgUmFuZ2VyIFVNSS9jZWxsIiwgeSA9ICJBbGV2aW4gRnJ5IFVuZmlsdGVyZWQgU0tldGNoIFVNSS9jZWxsIikgKyAKICB0aGVtZV9jbGFzc2ljKCkKYGBgClNvIG5vdyB3ZSBjYW4gc2VlIHdoeSB0aGVzZSBjb3JyZWxhdGlvbnMgYXJlIHNvIG9mZiwgZXNwZWNpYWxseSBmb3IgMTE4LCB0aGVyZSBhcmUgdmVyeSBmZXcgc2hhcmVkIGNlbGwgYmFyY29kZXMuICAKCmBgYHtyfQpnZ3Bsb3QobnVjbGV1c19xY19jb21tb25fY29yLCBhZXMoeCA9IGBjZWxscmFuZ2VyX3ByZS1tUk5BYCwgeSA9IGBrYWxsaXN0b19wcmUtbVJOQWApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBmYWNldF93cmFwKH4gc2FtcGxlKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIAogIHNjYWxlX3lfbG9nMTAoKSArIAogIGxhYnMoeCA9ICJDZWxsIFJhbmdlciBVTUkvY2VsbCIsIHkgPSAiS2FsbGlzdG8gVU1JL2NlbGwiKSArIAogIHRoZW1lX2NsYXNzaWMoKQpgYGAKSGVyZSB5b3UgY2FuIHNlZSB0aGF0IGZvciAxMTksIEthbGxpc3RvIGhhcyBnZW5lcmFsbHkgbXVjaCBoaWdoZXIgVU1JL2NlbGwsIGJ1dCBoaWdoZXIgdmFyaWFuY2UgdGhhbiBBbGV2aW4tRnJ5LVVuZmlsdGVyZWQtU2tldGNoLiAKCiMjIFNlc3Npb25JbmZvCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=